#ifndef PONG_CONTROLLER_H
#define PONG_CONTROLLER_H

#include <OgreCamera.h>
#include <OgreFrameListener.h>
#include <OgreRoot.h>

#include <OIS.h>

namespace pong {
	///The Controller listens for input events and moves the node accordingly.
	class Controller : public OIS::MouseListener, public OIS::KeyListener, public Ogre::FrameListener {
		protected:
			//Devices to listen to
			OIS::Mouse* listenMouse;
			OIS::Keyboard* listenKeys;

			//OGRE rendering root
			Ogre::Root* _root;

			float rotationFactor, //Factor for rotations so it isn't too fast
				  moveSpeed;      //Speed of translational motion
			Ogre::Node* controlNode; //Node which we control
			Ogre::Vector3 moveVec, rotateVec; //Movement/rotation vectors for persistency

			bool mousePanCamera; //Whether or not mouse movement pans the camera

		public:
			/**
				@arg objNode  	   The node which this controller will act upon
				@arg keyboard 	   A pointer to the OIS::Keyboard pointer to listen to
				@arg mouse    	   A pointer to he OIS::Mouse object to listen to
				@arg speed         The speed of translational movement
				@arg rotationSpeed The number of radians to rotate
 			**/
			Controller(Ogre::Node* objNode, Ogre::Root* root,
							 OIS::Keyboard* keyboard, OIS::Mouse* mouse,
							 float speed = 10.0f, float rotationSpeed =.02f)
					: listenKeys(keyboard), listenMouse(mouse),
					  _root(root),
					  controlNode(objNode),
					  moveSpeed(speed), rotationFactor(rotationSpeed),
					  moveVec(Ogre::Vector3::ZERO), rotateVec(Ogre::Vector3::ZERO),
					  mousePanCamera(false)
			{
				listenKeys->setEventCallback(this);
				listenMouse->setEventCallback(this);

				_root->addFrameListener(this);
			}
			private:
				///Copy contructor. Copies are forbidden due to OIS, so it is private
				Controller(const Controller& other) {}

			public:
			~Controller(void) {
				listenKeys->setEventCallback(0);
				listenMouse->setEventCallback(0);

				_root->removeFrameListener(this);
			}

			//Key control
			bool keyPressed(const OIS::KeyEvent& evt) {
				//WASD + UP/DOWN camera motion
				switch(evt.key) {
					case OIS::KC_W:
						moveVec.z -= moveSpeed;
						break;
					case OIS::KC_S:
						moveVec.z += moveSpeed;
						break;
					case OIS::KC_A:
						moveVec.x -= moveSpeed;
						break;
					case OIS::KC_D:
						moveVec.x += moveSpeed;
						break;
					case OIS::KC_UP:
						moveVec.y += moveSpeed;
						break;
					case OIS::KC_DOWN:
						moveVec.y -= moveSpeed;
						break;
					//LEFT/RIGHT rotate the camera
					case OIS::KC_LEFT:
						rotateVec.x -= rotationFactor;
						break;
					case OIS::KC_RIGHT:
						rotateVec.x += rotationFactor;
						break;
					default:
						break;
				}
				return true;
			}
			bool keyReleased(const OIS::KeyEvent& evt) {
				switch(evt.key) {
					//WASD + UP/DOWN translates the camera
					case OIS::KC_W:
						moveVec.z += moveSpeed;
						break;
					case OIS::KC_S:
						moveVec.z -= moveSpeed;
						break;
					case OIS::KC_A:
						moveVec.x += moveSpeed;
						break;
					case OIS::KC_D:
						moveVec.x -= moveSpeed;
						break;
					case OIS::KC_UP:
						moveVec.y -= moveSpeed;
						break;
					case OIS::KC_DOWN:
						moveVec.y += moveSpeed;
						break;
					case OIS::KC_LEFT:
						rotateVec.x += rotationFactor;
						break;
					case OIS::KC_RIGHT:
						rotateVec.x -= rotationFactor;
						break;
					default:
						break;
				}
				return true;
			}

			//Applying movement through the FrameListener
			bool frameStarted(const Ogre::FrameEvent& e) {
				listenMouse->capture();
				listenKeys->capture();
                controlNode->translate(controlNode->getOrientation() * moveVec * e.timeSinceLastFrame);
				return true;
			}

			//Mouse control
			bool mouseMoved(const OIS::MouseEvent& evt) {
				if(!mousePanCamera) {
					controlNode->yaw(Ogre::Radian(-rotationFactor * evt.state.X.rel), Ogre::SceneNode::TS_WORLD);
					controlNode->pitch(Ogre::Radian(-rotationFactor * evt.state.Y.rel));
				} else {
					moveVec = Ogre::Vector3(evt.state.X.rel, -evt.state.Y.rel, 0) * moveSpeed;
				}
				return true;
			}
			bool mousePressed(const OIS::MouseEvent& evt, OIS::MouseButtonID id) {
				switch(id) {
					case OIS::MB_Right:
						mousePanCamera = true;
						break;
					default: break;
				}
				return true;
			}
			bool mouseReleased(const OIS::MouseEvent& evt, OIS::MouseButtonID id) {
				switch(id) {
					case OIS::MB_Right:
						mousePanCamera = false;
                        moveVec = Ogre::Vector3::ZERO;
						break;
					default: break;
				}
				return true;
			}
	};
}
#endif
